package com.dmj.sqldsl.platform;

import static com.dmj.sqldsl.util.FileUtils.getUri;

import com.dmj.sqldsl.executor.ConnectionManager;
import com.dmj.sqldsl.executor.SqlDialect;
import com.dmj.sqldsl.executor.exception.ExecutionException;
import com.dmj.sqldsl.util.FileUtils;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.function.Consumer;
import lombok.Getter;
import lombok.RequiredArgsConstructor;

@Getter
@RequiredArgsConstructor
public class DatabaseManager implements ConnectionManager {

  private final DatabaseContainer container;

  public void execStmt(Consumer<Statement> consumer) {
    try (Connection connection = getConnection()) {
      try (Statement stmt = connection.createStatement()) {
        consumer.accept(stmt);
      }
    } catch (SQLException e) {
      throw new RuntimeException(e);
    }
  }

  public void executeSqlFile(String fileName) {
    execStmt(statement -> {
      StringBuilder builder = new StringBuilder();
      FileUtils.readLines(getUri(fileName + "/" + getDialect() + ".sql"), line -> {
        builder.append(line);
        if (line.trim().isEmpty()) {
          execute(statement, builder.toString());
          builder.delete(0, builder.length());
        }
      });
      execute(statement, builder.toString());
    });
  }

  private static void execute(Statement statement, String sql) {
    try {
      statement.execute(sql);
    } catch (SQLException e) {
      throw new RuntimeException(e);
    }
  }

  public void cleanDatabase() {
    try (Connection connection = getConnection()) {
      try (ResultSet tables = connection.getMetaData()
          .getTables(null, null, "%", new String[]{"TABLE"})) {
        try (Statement statement = connection.createStatement()) {
          while (tables.next()) {
            String tableName = tables.getString("TABLE_NAME");
            if (tableName.toUpperCase().startsWith("TEST_")) {
              statement.execute("DROP TABLE " + tableName);
            }
          }
        }
      }
    } catch (SQLException e) {
      throw new RuntimeException(e);
    }
  }

  public Connection getConnection() {
    try {
      return DriverManager.getConnection(container.getUrl(), container.getUser(),
          container.getPassword());
    } catch (SQLException e) {
      throw new ExecutionException(e);
    }
  }

  public SqlDialect getDialect() {
    return this.getContainer().getMode().toSqlDialect();
  }

  public String getDisplayName() {
    return this.container.getDisplayName();
  }
}
